package com.config;

import org.aopalliance.aop.Advice;
import org.h2.jdbcx.JdbcDataSource;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.*;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;

import static org.springframework.transaction.TransactionDefinition.ISOLATION_READ_COMMITTED;
import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED;

@Configuration
public class TransactionConfig {

    @Bean
    public DataSource dataSource() {
        JdbcDataSource dataSource = new JdbcDataSource();
        dataSource.setURL("jdbc:h2:./spring_transaction_database");
        dataSource.setUser("username");
        dataSource.setPassword("password");

        try (Connection connection = dataSource.getConnection()) {

            Statement statement = connection.createStatement();
            statement.execute("CREATE TABLE  IF NOT EXISTS `user` ( `id` int auto_increment, `name` varchar(10), `dept_id` int, PRIMARY KEY (`id`) ) ");
            statement.execute("TRUNCATE TABLE user");
            statement.execute("CREATE TABLE  IF NOT EXISTS `dept` ( `id` int auto_increment, `name` varchar(10), PRIMARY KEY (`id`) ) ");
            statement.execute("TRUNCATE TABLE dept");

            statement.execute("CREATE TABLE  IF NOT EXISTS `order_t` ( `id` int auto_increment, `name` varchar(10),`status` tinyint,`create_dept_id` int,`create_user_id` int,`create_time` date , PRIMARY KEY (`id`) ) ");
            statement.execute("TRUNCATE TABLE order_t");
            statement.close();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        //TODO 查看 LazyConnectionDataSourceProxy 源码
        return new LazyConnectionDataSourceProxy(dataSource);
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {

        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    /**
     * 定义事务执行的 Advice
     * 如果目标事务方法中有 {@link Transactional} 注解则从其中解析 事务属性
     * <p>
     * 否则使用 默认的事务属性配置
     *
     * @param platformTransactionManager
     * @param transactionAttributeSource 默认的事务属性配置
     * @return
     */
    @Bean
    public Advice transactionInterceptorAdvice(PlatformTransactionManager platformTransactionManager, TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

        transactionInterceptor.setTransactionManager(platformTransactionManager);

//        transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);

        transactionInterceptor.setTransactionAttributeSources(
               /*
                    这个实现来从 @Transactional 中解析对应的 事务定义
                */
                new AnnotationTransactionAttributeSource(false),

                // 默认的事务属性
                transactionAttributeSource
        );
        return transactionInterceptor;
    }



    /**
     * 定义事务执行的切面
     *
     * @param transactionInterceptorAdvice
     * @return
     */
    @Bean
    public Advisor transactionAdvisor(Advice transactionInterceptorAdvice) {

        // 用于 class 层级有 @Transactional 注解的情况
        Pointcut classTransactional = new AnnotationMatchingPointcut(Transactional.class,null);

        // 用于 method 层级有 @Transactional 注解的情况
        Pointcut methodTransactional = new AnnotationMatchingPointcut(null,Transactional.class);

        /*
        AspectJExpressionPointcut pathMatchingPointcut = new AspectJExpressionPointcut();

        // 添加不存在的前缀 使得 AspectJ 表达式匹配失效
        pathMatchingPointcut.setExpression("execution(* x.y.com.service.*.*(..))");

//        组合 Transactional 匹配事务方法 和 AspectJ 切点方法匹配事务方法
*/
        // 组合 class 层级 和 method 层级，两者之间是 或的逻辑关系
        ComposablePointcut composablePointcut = new ComposablePointcut(classTransactional).union( methodTransactional);

        return new DefaultPointcutAdvisor(composablePointcut, transactionInterceptorAdvice);

    }

    /**
     * 设置每个方法对应事务的属性映射关系
     * 这里配置所有方法都使用统一的事务属性
     *
     * @param transactionAttribute
     * @return
     */
    @Bean
    public TransactionAttributeSource transactionAttributeSource(TransactionAttribute transactionAttribute) {
        MatchAlwaysTransactionAttributeSource result = new MatchAlwaysTransactionAttributeSource();

        result.setTransactionAttribute(transactionAttribute);

        return result;
    }

    /**
     * 定义事务的属性
     * <ul>
     *     <li>隔离级别</li>
     *     <li>传播行为</li>
     *     <li>超时时间</li>
     * </ul>
     *
     * @return
     */
    @Bean
    public TransactionAttribute transactionAttribute() {
        DefaultTransactionAttribute result = new DefaultTransactionAttribute();
        result.setIsolationLevel(ISOLATION_READ_COMMITTED);
        result.setPropagationBehavior(PROPAGATION_REQUIRED);

        return result;
    }


}
